﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using PacManLib;

namespace PacManLib
{
    /// <summary>
    /// Loads and manages the collection of AIs. 
    /// This class is a singleton
    /// </summary>
    public class AIManager
    {
        #region Public methods

        /// <summary>
        /// The singleton getInstance() method.
        /// </summary>
        public static AIManager getInstance()
        {
            if (m_instance == null)
            {
                m_instance = new AIManager();
            }
            return m_instance;
        }

        /// <summary>
        /// Returns a collection of the Pac-Man AI names.
        /// </summary>
        public string[] getPacManAINames()
        {
            return m_mapNamesToPacManAIs.Keys.ToArray();
        }

        /// <summary>
        /// Returns a collection of the ghost AI names.
        /// </summary>
        public string[] getGhostAINames()
        {
            return m_mapNamesToGhostAIs.Keys.ToArray();
        }

        /// <summary>
        /// Returns a new instance of the Pac-Man AI whose name is passed in.
        /// </summary>
        public IPacManAI getNewPacManAIInstance(string aiName)
        {
            return (IPacManAI)createAI(m_mapNamesToPacManAIs, aiName);
        }

        /// <summary>
        /// Returns a new instance of the ghost AI whose name is passed in.
        /// </summary>
        public IGhostAI getNewGhostAIInstance(string aiName)
        {
            return (IGhostAI)createAI(m_mapNamesToGhostAIs, aiName);
        }

        #endregion

        #region Private functions

        /// <summary>
        /// Constructor.
        /// </summary>
        private AIManager()
        {
            Log.log("Loading AIs");

            // We look for all AI dlls in the working folder...
            string[] dlls = Directory.GetFiles(".", "PacManAI_*.dll");
            foreach (string assemblyName in dlls)
            {
                try
                {
                    // We get the AI class type, ie the type of the class
                    // implementing IPlayerAI...
                    Log.log(String.Format("Loading dll: {0}", assemblyName));

                    // We look for a Pac-Man AI...
                    Type pacManAIType = ReflectionUtils.getPacManAIFromAssembly(assemblyName);
                    if (pacManAIType != null)
                    {
                        // We create an instance to get the name from it, and store it...
                        IPacManAI ai = (IPacManAI)Activator.CreateInstance(pacManAIType);
                        string aiName = Utility.getAILongName(ai);
                        m_mapNamesToPacManAIs.Add(aiName, pacManAIType);
                    }

                    // We look for a ghost AI...
                    Type ghostAIType = ReflectionUtils.getGhostAIFromAssembly(assemblyName);
                    if (ghostAIType != null)
                    {
                        // We create an instance to get the name from it, and store it...
                        IGhostAI ai = (IGhostAI)Activator.CreateInstance(ghostAIType);
                        string aiName = Utility.getAILongName(ai);
                        m_mapNamesToGhostAIs.Add(aiName, ghostAIType);
                    }
                }
                catch(Exception ex)
                {
                    Log.log(String.Format("Failed to load dll: {0}, error={1}", assemblyName, ex.Message));
                }
            }
        }

        /// <summary>
        /// Creates an AI for the name passed in.
        /// </summary>
        private object createAI(Dictionary<string, Type> dictionary, string aiName)
        {
            if (String.IsNullOrEmpty(aiName))
            {
                throw new Exception("createAI: empty string");
            }
            if (dictionary.ContainsKey(aiName) == false)
            {
                throw new Exception("createAI: AI name not registered " + aiName);
            }
            Type aiType = dictionary[aiName];
            return Activator.CreateInstance(aiType);
        }

        #endregion


        #region Private data

        // The singleton instance...
        private static AIManager m_instance = null;

        // A map of AI names to their types.
        // We can use the types to create instances of the AIs.
        private Dictionary<string, Type> m_mapNamesToPacManAIs = new Dictionary<string, Type>();
        private Dictionary<string, Type> m_mapNamesToGhostAIs = new Dictionary<string, Type>();

        #endregion
    }
}
